/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef FLATBUFFERS_IDL_H_
#define FLATBUFFERS_IDL_H_

#include <map>
#include <memory>
#include <stack>

#include "flatbuffers/base.h"
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/flexbuffers.h"
#include "flatbuffers/hash.h"
#include "flatbuffers/reflection.h"

#if !defined(FLATBUFFERS_CPP98_STL)
#include <functional>
#endif    // !defined(FLATBUFFERS_CPP98_STL)

// This file defines the data types representing a parsed IDL (Interface
// Definition Language) / schema file.

namespace flatbuffers {

// The order of these matters for Is*() functions below.
// Additionally, Parser::ParseType assumes bool..string is a contiguous range
// of type tokens.
// clang-format off
#define FLATBUFFERS_GEN_TYPES_SCALAR(TD) \
  TD(NONE,   "",       uint8_t,  byte,   byte,    byte,   uint8) \
  TD(UTYPE,  "",       uint8_t,  byte,   byte,    byte,   uint8) /* begin scalar/int */ \
  TD(BOOL,   "bool",   uint8_t,  boolean,byte,    bool,   bool) \
  TD(CHAR,   "byte",   int8_t,   byte,   int8,    sbyte,  int8) \
  TD(UCHAR,  "ubyte",  uint8_t,  byte,   byte,    byte,   uint8) \
  TD(SHORT,  "short",  int16_t,  short,  int16,   short,  int16) \
  TD(USHORT, "ushort", uint16_t, short,  uint16,  ushort, uint16) \
  TD(INT,    "int",    int32_t,  int,    int32,   int,    int32) \
  TD(UINT,   "uint",   uint32_t, int,    uint32,  uint,   uint32) \
  TD(LONG,   "long",   int64_t,  long,   int64,   long,   int64) \
  TD(ULONG,  "ulong",  uint64_t, long,   uint64,  ulong,  uint64) /* end int */ \
  TD(FLOAT,  "float",  float,    float,  float32, float,  float32) /* begin float */ \
  TD(DOUBLE, "double", double,   double, float64, double, float64) /* end float/scalar */
#define FLATBUFFERS_GEN_TYPES_POINTER(TD) \
  TD(STRING, "string", Offset<void>, int, int, StringOffset, int) \
  TD(VECTOR, "",       Offset<void>, int, int, VectorOffset, int) \
  TD(STRUCT, "",       Offset<void>, int, int, int, int) \
  TD(UNION,  "",       Offset<void>, int, int, int, int)

// The fields are:
// - enum
// - FlatBuffers schema type.
// - C++ type.
// - Java type.
// - Go type.
// - C# / .Net type.
// - Python type.

// using these macros, we can now write code dealing with types just once, e.g.

/*
switch (type) {
  #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
    case BASE_TYPE_ ## ENUM: \
      // do something specific to CTYPE here
    FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
  #undef FLATBUFFERS_TD
}
*/

#define FLATBUFFERS_GEN_TYPES(TD) \
        FLATBUFFERS_GEN_TYPES_SCALAR(TD) \
        FLATBUFFERS_GEN_TYPES_POINTER(TD)

// Create an enum for all the types above.
#ifdef __GNUC__
__extension__  // Stop GCC complaining about trailing comma with -Wpendantic.
#endif
enum BaseType {
  #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
      BASE_TYPE_ ## ENUM,
    FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
  #undef FLATBUFFERS_TD
};

#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
    static_assert(sizeof(CTYPE) <= sizeof(largest_scalar_t), \
                  "define largest_scalar_t as " #CTYPE);
  FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD

inline bool IsScalar (BaseType t) { return t >= BASE_TYPE_UTYPE &&
                                           t <= BASE_TYPE_DOUBLE; }
inline bool IsInteger(BaseType t) { return t >= BASE_TYPE_UTYPE &&
                                           t <= BASE_TYPE_ULONG; }
inline bool IsFloat  (BaseType t) { return t == BASE_TYPE_FLOAT ||
                                           t == BASE_TYPE_DOUBLE; }
inline bool IsLong   (BaseType t) { return t == BASE_TYPE_LONG ||
                                           t == BASE_TYPE_ULONG; }
inline bool IsBool   (BaseType t) { return t == BASE_TYPE_BOOL; }
// clang-format on

extern const char* const kTypeNames[];
extern const char kTypeSizes[];

inline size_t SizeOf(BaseType t)
{
    return kTypeSizes[t];
}

struct StructDef;
struct EnumDef;
class Parser;

// Represents any type in the IDL, which is a combination of the BaseType
// and additional information for vectors/structs_.
struct Type
{
    explicit Type(BaseType _base_type = BASE_TYPE_NONE, StructDef* _sd = nullptr, EnumDef* _ed = nullptr)
        : base_type(_base_type), element(BASE_TYPE_NONE), struct_def(_sd), enum_def(_ed)
    {
    }

    bool operator==(const Type& o)
    {
        return base_type == o.base_type && element == o.element && struct_def == o.struct_def && enum_def == o.enum_def;
    }

    Type VectorType() const
    {
        return Type(element, struct_def, enum_def);
    }

    Offset<reflection::Type> Serialize(FlatBufferBuilder* builder) const;

    BaseType base_type;
    BaseType element;    // only set if t == BASE_TYPE_VECTOR
    StructDef* struct_def;    // only set if t or element == BASE_TYPE_STRUCT
    EnumDef* enum_def;    // set if t == BASE_TYPE_UNION / BASE_TYPE_UTYPE,
    // or for an integral type derived from an enum.
};

// Represents a parsed scalar value, it's type, and field offset.
struct Value
{
    Value() : constant("0"), offset(static_cast<voffset_t>(~(static_cast<voffset_t>(0U)))) {}
    Type type;
    std::string constant;
    voffset_t offset;
};

// Helper class that retains the original order of a set of identifiers and
// also provides quick lookup.
template <typename T> class SymbolTable
{
public:
    ~SymbolTable()
    {
        for(auto it = vec.begin(); it != vec.end(); ++it)
        {
            delete *it;
        }
    }

    bool Add(const std::string& name, T* e)
    {
        vector_emplace_back(&vec, e);
        auto it = dict.find(name);
        if(it != dict.end())
            return true;
        dict[name] = e;
        return false;
    }

    void Move(const std::string& oldname, const std::string& newname)
    {
        auto it = dict.find(oldname);
        if(it != dict.end())
        {
            auto obj = it->second;
            dict.erase(it);
            dict[newname] = obj;
        }
        else
        {
            assert(false);
        }
    }

    T* Lookup(const std::string& name) const
    {
        auto it = dict.find(name);
        return it == dict.end() ? nullptr : it->second;
    }

public:
    std::map<std::string, T*> dict;    // quick lookup
    std::vector<T*> vec;    // Used to iterate in order of insertion
};

// A name space, as set in the schema.
struct Namespace
{
    Namespace() : from_table(0) {}

    // Given a (potentally unqualified) name, return the "fully qualified" name
    // which has a full namespaced descriptor.
    // With max_components you can request less than the number of components
    // the current namespace has.
    std::string GetFullyQualifiedName(const std::string& name, size_t max_components = 1000) const;

    std::vector<std::string> components;
    size_t from_table;    // Part of the namespace corresponds to a message/table.
};

// Base class for all definition types (fields, structs_, enums_).
struct Definition
{
    Definition() : generated(false), defined_namespace(nullptr), serialized_location(0), index(-1), refcount(1) {}

    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>>>
    SerializeAttributes(FlatBufferBuilder* builder, const Parser& parser) const;

    std::string name;
    std::string file;
    std::vector<std::string> doc_comment;
    SymbolTable<Value> attributes;
    bool generated;    // did we already output code for this definition?
    Namespace* defined_namespace;    // Where it was defined.

    // For use with Serialize()
    uoffset_t serialized_location;
    int index;    // Inside the vector it is stored.
    int refcount;
};

struct FieldDef : public Definition
{
    FieldDef()
        : deprecated(false), required(false), key(false), native_inline(false), flexbuffer(false),
          nested_flatbuffer(NULL), padding(0)
    {
    }

    Offset<reflection::Field> Serialize(FlatBufferBuilder* builder, uint16_t id, const Parser& parser) const;

    Value value;
    bool deprecated;    // Field is allowed to be present in old data, but can't be.
    // written in new data nor accessed in new code.
    bool required;    // Field must always be present.
    bool key;    // Field functions as a key for creating sorted vectors.
    bool native_inline;    // Field will be defined inline (instead of as a pointer)
    // for native tables if field is a struct.
    bool flexbuffer;    // This field contains FlexBuffer data.
    StructDef* nested_flatbuffer;    // This field contains nested FlatBuffer data.
    size_t padding;    // Bytes to always pad after this field.
};

struct StructDef : public Definition
{
    StructDef() : fixed(false), predecl(true), sortbysize(true), has_key(false), minalign(1), bytesize(0) {}

    void PadLastField(size_t min_align)
    {
        auto padding = PaddingBytes(bytesize, min_align);
        bytesize += padding;
        if(fields.vec.size())
            fields.vec.back()->padding = padding;
    }

    Offset<reflection::Object> Serialize(FlatBufferBuilder* builder, const Parser& parser) const;

    SymbolTable<FieldDef> fields;

    bool fixed;    // If it's struct, not a table.
    bool predecl;    // If it's used before it was defined.
    bool sortbysize;    // Whether fields come in the declaration or size order.
    bool has_key;    // It has a key field.
    size_t minalign;    // What the whole object needs to be aligned to.
    size_t bytesize;    // Size if fixed.

    flatbuffers::unique_ptr<std::string> original_location;
};

inline bool IsStruct(const Type& type)
{
    return type.base_type == BASE_TYPE_STRUCT && type.struct_def->fixed;
}

inline size_t InlineSize(const Type& type)
{
    return IsStruct(type) ? type.struct_def->bytesize : SizeOf(type.base_type);
}

inline size_t InlineAlignment(const Type& type)
{
    return IsStruct(type) ? type.struct_def->minalign : SizeOf(type.base_type);
}

struct EnumVal
{
    EnumVal(const std::string& _name, int64_t _val) : name(_name), value(_val) {}

    Offset<reflection::EnumVal> Serialize(FlatBufferBuilder* builder) const;

    std::string name;
    std::vector<std::string> doc_comment;
    int64_t value;
    Type union_type;
};

struct EnumDef : public Definition
{
    EnumDef() : is_union(false), uses_type_aliases(false) {}

    EnumVal* ReverseLookup(int64_t enum_idx, bool skip_union_default = true)
    {
        for(auto it = vals.vec.begin() + static_cast<int>(is_union && skip_union_default); it != vals.vec.end(); ++it)
        {
            if((*it)->value == enum_idx)
            {
                return *it;
            }
        }
        return nullptr;
    }

    Offset<reflection::Enum> Serialize(FlatBufferBuilder* builder, const Parser& parser) const;

    SymbolTable<EnumVal> vals;
    bool is_union;
    bool uses_type_aliases;
    Type underlying_type;
};

inline bool EqualByName(const Type& a, const Type& b)
{
    return a.base_type == b.base_type && a.element == b.element &&
           (a.struct_def == b.struct_def || a.struct_def->name == b.struct_def->name) &&
           (a.enum_def == b.enum_def || a.enum_def->name == b.enum_def->name);
}

struct RPCCall
{
    std::string name;
    SymbolTable<Value> attributes;
    StructDef *request, *response;
    std::vector<std::string> rpc_comment;
};

struct ServiceDef : public Definition
{
    SymbolTable<RPCCall> calls;
};

// Container of options that may apply to any of the source/text generators.
struct IDLOptions
{
    bool strict_json;
    bool skip_js_exports;
    bool use_goog_js_export_format;
    bool output_default_scalars_in_json;
    int indent_step;
    bool output_enum_identifiers;
    bool prefixed_enums;
    bool scoped_enums;
    bool include_dependence_headers;
    bool mutable_buffer;
    bool one_file;
    bool proto_mode;
    bool proto_oneof_union;
    bool generate_all;
    bool skip_unexpected_fields_in_json;
    bool generate_name_strings;
    bool generate_object_based_api;
    std::string cpp_object_api_pointer_type;
    std::string cpp_object_api_string_type;
    bool gen_nullable;
    std::string object_prefix;
    std::string object_suffix;
    bool union_value_namespacing;
    bool allow_non_utf8;
    std::string include_prefix;
    bool keep_include_path;
    bool binary_schema_comments;
    bool skip_flatbuffers_import;
    std::string go_import;
    std::string go_namespace;
    bool reexport_ts_modules;
    bool protobuf_ascii_alike;
    bool size_prefixed;

    // Possible options for the more general generator below.
    enum Language
    {
        kJava = 1 << 0,
        kCSharp = 1 << 1,
        kGo = 1 << 2,
        kCpp = 1 << 3,
        kJs = 1 << 4,
        kPython = 1 << 5,
        kPhp = 1 << 6,
        kJson = 1 << 7,
        kBinary = 1 << 8,
        kTs = 1 << 9,
        kJsonSchema = 1 << 10,
        kMAX
    };

    Language lang;

    enum MiniReflect
    {
        kNone,
        kTypes,
        kTypesAndNames
    };

    MiniReflect mini_reflect;

    // The corresponding language bit will be set if a language is included
    // for code generation.
    unsigned long lang_to_generate;

    IDLOptions()
        : strict_json(false), skip_js_exports(false), use_goog_js_export_format(false),
          output_default_scalars_in_json(false), indent_step(2), output_enum_identifiers(true), prefixed_enums(true),
          scoped_enums(false), include_dependence_headers(true), mutable_buffer(false), one_file(false),
          proto_mode(false), proto_oneof_union(false), generate_all(false), skip_unexpected_fields_in_json(false),
          generate_name_strings(false), generate_object_based_api(false),
          cpp_object_api_pointer_type("std::unique_ptr"), gen_nullable(false), object_suffix("T"),
          union_value_namespacing(true), allow_non_utf8(false), keep_include_path(false), binary_schema_comments(false),
          skip_flatbuffers_import(false), reexport_ts_modules(true), protobuf_ascii_alike(false), size_prefixed(false),
          lang(IDLOptions::kJava), mini_reflect(IDLOptions::kNone), lang_to_generate(0)
    {
    }
};

// This encapsulates where the parser is in the current source file.
struct ParserState
{
    ParserState() : cursor_(nullptr), line_(1), token_(-1) {}

protected:
    const char* cursor_;
    int line_;    // the current line being parsed
    int token_;

    std::string attribute_;
    std::vector<std::string> doc_comment_;
};

// A way to make error propagation less error prone by requiring values to be
// checked.
// Once you create a value of this type you must either:
// - Call Check() on it.
// - Copy or assign it to another value.
// Failure to do so leads to an assert.
// This guarantees that this as return value cannot be ignored.
class CheckedError
{
public:
    explicit CheckedError(bool error) : is_error_(error), has_been_checked_(false) {}

    CheckedError& operator=(const CheckedError& other)
    {
        is_error_ = other.is_error_;
        has_been_checked_ = false;
        other.has_been_checked_ = true;
        return *this;
    }

    CheckedError(const CheckedError& other)
    {
        *this = other;    // Use assignment operator.
    }

    ~CheckedError()
    {
        assert(has_been_checked_);
    }

    bool Check()
    {
        has_been_checked_ = true;
        return is_error_;
    }

private:
    bool is_error_;
    mutable bool has_been_checked_;
};

// Additionally, in GCC we can get these errors statically, for additional
// assurance:
// clang-format off
#ifdef __GNUC__
#define FLATBUFFERS_CHECKED_ERROR CheckedError \
          __attribute__((warn_unused_result))
#else
#define FLATBUFFERS_CHECKED_ERROR CheckedError
#endif
// clang-format on

class Parser : public ParserState
{
public:
    explicit Parser(const IDLOptions& options = IDLOptions())
        : current_namespace_(nullptr), empty_namespace_(nullptr), root_struct_def_(nullptr), opts(options),
          uses_flexbuffers_(false), source_(nullptr), anonymous_counter(0)
    {
        // Start out with the empty namespace being current.
        empty_namespace_ = new Namespace();
        namespaces_.push_back(empty_namespace_);
        current_namespace_ = empty_namespace_;
        known_attributes_["deprecated"] = true;
        known_attributes_["required"] = true;
        known_attributes_["key"] = true;
        known_attributes_["hash"] = true;
        known_attributes_["id"] = true;
        known_attributes_["force_align"] = true;
        known_attributes_["bit_flags"] = true;
        known_attributes_["original_order"] = true;
        known_attributes_["nested_flatbuffer"] = true;
        known_attributes_["csharp_partial"] = true;
        known_attributes_["streaming"] = true;
        known_attributes_["idempotent"] = true;
        known_attributes_["cpp_type"] = true;
        known_attributes_["cpp_ptr_type"] = true;
        known_attributes_["cpp_ptr_type_get"] = true;
        known_attributes_["cpp_str_type"] = true;
        known_attributes_["native_inline"] = true;
        known_attributes_["native_custom_alloc"] = true;
        known_attributes_["native_type"] = true;
        known_attributes_["native_default"] = true;
        known_attributes_["flexbuffer"] = true;
    }

    ~Parser()
    {
        for(auto it = namespaces_.begin(); it != namespaces_.end(); ++it)
        {
            delete *it;
        }
    }

    // Parse the string containing either schema or JSON data, which will
    // populate the SymbolTable's or the FlatBufferBuilder above.
    // include_paths is used to resolve any include statements, and typically
    // should at least include the project path (where you loaded source_ from).
    // include_paths must be nullptr terminated if specified.
    // If include_paths is nullptr, it will attempt to load from the current
    // directory.
    // If the source was loaded from a file and isn't an include file,
    // supply its name in source_filename.
    // All paths specified in this call must be in posix format, if you accept
    // paths from user input, please call PosixPath on them first.
    bool Parse(const char* _source, const char** include_paths = nullptr, const char* source_filename = nullptr);

    // Set the root type. May override the one set in the schema.
    bool SetRootType(const char* name);

    // Mark all definitions as already having code generated.
    void MarkGenerated();

    // Get the files recursively included by the given file. The returned
    // container will have at least the given file.
    std::set<std::string> GetIncludedFilesRecursive(const std::string& file_name) const;

    // Fills builder_ with a binary version of the schema parsed.
    // See reflection/reflection.fbs
    void Serialize();

    // Checks that the schema represented by this parser is a safe evolution
    // of the schema provided. Returns non-empty error on any problems.
    std::string ConformTo(const Parser& base);

    // Similar to Parse(), but now only accepts JSON to be parsed into a
    // FlexBuffer.
    bool ParseFlexBuffer(const char* source, const char* source_filename, flexbuffers::Builder* builder);

    FLATBUFFERS_CHECKED_ERROR CheckInRange(int64_t val, int64_t min, int64_t max);

    StructDef* LookupStruct(const std::string& id) const;

private:
    void Message(const std::string& msg);
    void Warning(const std::string& msg);
    FLATBUFFERS_CHECKED_ERROR Error(const std::string& msg);
    FLATBUFFERS_CHECKED_ERROR ParseHexNum(int nibbles, uint64_t* val);
    FLATBUFFERS_CHECKED_ERROR Next();
    FLATBUFFERS_CHECKED_ERROR SkipByteOrderMark();
    bool Is(int t) const;
    bool IsIdent(const char* id) const;
    FLATBUFFERS_CHECKED_ERROR Expect(int t);
    std::string TokenToStringId(int t) const;
    EnumDef* LookupEnum(const std::string& id);
    FLATBUFFERS_CHECKED_ERROR ParseNamespacing(std::string* id, std::string* last);
    FLATBUFFERS_CHECKED_ERROR ParseTypeIdent(Type& type);
    FLATBUFFERS_CHECKED_ERROR ParseType(Type& type);
    FLATBUFFERS_CHECKED_ERROR AddField(StructDef& struct_def, const std::string& name, const Type& type,
                                       FieldDef** dest);
    FLATBUFFERS_CHECKED_ERROR ParseField(StructDef& struct_def);
    FLATBUFFERS_CHECKED_ERROR ParseString(Value& val);
    FLATBUFFERS_CHECKED_ERROR ParseComma();
    FLATBUFFERS_CHECKED_ERROR ParseAnyValue(Value& val, FieldDef* field, size_t parent_fieldn,
                                            const StructDef* parent_struct_def);
    // clang-format off
  #if defined(FLATBUFFERS_CPP98_STL)
    typedef CheckedError (*ParseTableDelimitersBody)(
        const std::string &name, size_t &fieldn, const StructDef *struct_def,
        void *state);
  #else
    typedef std::function<CheckedError(const std::string&, size_t&,
                                       const StructDef*, void*)>
            ParseTableDelimitersBody;
  #endif  // defined(FLATBUFFERS_CPP98_STL)
    // clang-format on
    FLATBUFFERS_CHECKED_ERROR ParseTableDelimiters(size_t& fieldn, const StructDef* struct_def,
                                                   ParseTableDelimitersBody body, void* state);
    FLATBUFFERS_CHECKED_ERROR ParseTable(const StructDef& struct_def, std::string* value, uoffset_t* ovalue);
    void SerializeStruct(const StructDef& struct_def, const Value& val);
    // clang-format off
  #if defined(FLATBUFFERS_CPP98_STL)
    typedef CheckedError (*ParseVectorDelimitersBody)(size_t &count,
                                                      void *state);
  #else
    typedef std::function<CheckedError(size_t&, void*)>
            ParseVectorDelimitersBody;
  #endif  // defined(FLATBUFFERS_CPP98_STL)
    // clang-format on
    FLATBUFFERS_CHECKED_ERROR ParseVectorDelimiters(size_t& count, ParseVectorDelimitersBody body, void* state);
    FLATBUFFERS_CHECKED_ERROR ParseVector(const Type& type, uoffset_t* ovalue);
    FLATBUFFERS_CHECKED_ERROR ParseNestedFlatbuffer(Value& val, FieldDef* field, size_t fieldn,
                                                    const StructDef* parent_struct_def);
    FLATBUFFERS_CHECKED_ERROR ParseMetaData(SymbolTable<Value>* attributes);
    FLATBUFFERS_CHECKED_ERROR TryTypedValue(const std::string* name, int dtoken, bool check, Value& e, BaseType req,
                                            bool* destmatch);
    FLATBUFFERS_CHECKED_ERROR ParseHash(Value& e, FieldDef* field);
    FLATBUFFERS_CHECKED_ERROR TokenError();
    FLATBUFFERS_CHECKED_ERROR ParseSingleValue(const std::string* name, Value& e);
    FLATBUFFERS_CHECKED_ERROR ParseEnumFromString(Type& type, int64_t* result);
    StructDef* LookupCreateStruct(const std::string& name, bool create_if_new = true, bool definition = false);
    FLATBUFFERS_CHECKED_ERROR ParseEnum(bool is_union, EnumDef** dest);
    FLATBUFFERS_CHECKED_ERROR ParseNamespace();
    FLATBUFFERS_CHECKED_ERROR StartStruct(const std::string& name, StructDef** dest);
    FLATBUFFERS_CHECKED_ERROR StartEnum(const std::string& name, bool is_union, EnumDef** dest);
    FLATBUFFERS_CHECKED_ERROR ParseDecl();
    FLATBUFFERS_CHECKED_ERROR ParseService();
    FLATBUFFERS_CHECKED_ERROR ParseProtoFields(StructDef* struct_def, bool isextend, bool inside_oneof);
    FLATBUFFERS_CHECKED_ERROR ParseProtoOption();
    FLATBUFFERS_CHECKED_ERROR ParseProtoKey();
    FLATBUFFERS_CHECKED_ERROR ParseProtoDecl();
    FLATBUFFERS_CHECKED_ERROR ParseProtoCurliesOrIdent();
    FLATBUFFERS_CHECKED_ERROR ParseTypeFromProtoType(Type* type);
    FLATBUFFERS_CHECKED_ERROR SkipAnyJsonValue();
    FLATBUFFERS_CHECKED_ERROR ParseFlexBufferValue(flexbuffers::Builder* builder);
    FLATBUFFERS_CHECKED_ERROR StartParseFile(const char* source, const char* source_filename);
    FLATBUFFERS_CHECKED_ERROR ParseRoot(const char* _source, const char** include_paths, const char* source_filename);
    FLATBUFFERS_CHECKED_ERROR DoParse(const char* _source, const char** include_paths, const char* source_filename,
                                      const char* include_filename);
    FLATBUFFERS_CHECKED_ERROR CheckClash(std::vector<FieldDef*>& fields, StructDef* struct_def, const char* suffix,
                                         BaseType baseType);

    bool SupportsVectorOfUnions() const;
    Namespace* UniqueNamespace(Namespace* ns);

public:
    SymbolTable<Type> types_;
    SymbolTable<StructDef> structs_;
    SymbolTable<EnumDef> enums_;
    SymbolTable<ServiceDef> services_;
    std::vector<Namespace*> namespaces_;
    Namespace* current_namespace_;
    Namespace* empty_namespace_;
    std::string error_;    // User readable error_ if Parse() == false

    FlatBufferBuilder builder_;    // any data contained in the file
    StructDef* root_struct_def_;
    std::string file_identifier_;
    std::string file_extension_;

    std::map<std::string, std::string> included_files_;
    std::map<std::string, std::set<std::string>> files_included_per_file_;
    std::vector<std::string> native_included_files_;

    std::map<std::string, bool> known_attributes_;

    IDLOptions opts;
    bool uses_flexbuffers_;

private:
    const char* source_;

    std::string file_being_parsed_;

    std::vector<std::pair<Value, FieldDef*>> field_stack_;

    int anonymous_counter;
};

// Utility functions for multiple generators:

extern std::string MakeCamel(const std::string& in, bool first = true);

// Generate text (JSON) from a given FlatBuffer, and a given Parser
// object that has been populated with the corresponding schema.
// If ident_step is 0, no indentation will be generated. Additionally,
// if it is less than 0, no linefeeds will be generated either.
// See idl_gen_text.cpp.
// strict_json adds "quotes" around field names if true.
// If the flatbuffer cannot be encoded in JSON (e.g., it contains non-UTF-8
// byte arrays in String values), returns false.
extern bool GenerateText(const Parser& parser, const void* flatbuffer, std::string* text);
extern bool GenerateTextFile(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate binary files from a given FlatBuffer, and a given Parser
// object that has been populated with the corresponding schema.
// See idl_gen_general.cpp.
extern bool GenerateBinary(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate a C++ header from the definitions in the Parser object.
// See idl_gen_cpp.
extern bool GenerateCPP(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate JavaScript or TypeScript code from the definitions in the Parser object.
// See idl_gen_js.
extern bool GenerateJS(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate Go files from the definitions in the Parser object.
// See idl_gen_go.cpp.
extern bool GenerateGo(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate Php code from the definitions in the Parser object.
// See idl_gen_php.
extern bool GeneratePhp(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate Python files from the definitions in the Parser object.
// See idl_gen_python.cpp.
extern bool GeneratePython(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate Json schema file
// See idl_gen_json_schema.cpp.
extern bool GenerateJsonSchema(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate Java/C#/.. files from the definitions in the Parser object.
// See idl_gen_general.cpp.
extern bool GenerateGeneral(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate a schema file from the internal representation, useful after
// parsing a .proto schema.
extern std::string GenerateFBS(const Parser& parser, const std::string& file_name);
extern bool GenerateFBS(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate a make rule for the generated JavaScript or TypeScript code.
// See idl_gen_js.cpp.
extern std::string JSMakeRule(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate a make rule for the generated C++ header.
// See idl_gen_cpp.cpp.
extern std::string CPPMakeRule(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate a make rule for the generated Java/C#/... files.
// See idl_gen_general.cpp.
extern std::string GeneralMakeRule(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate a make rule for the generated text (JSON) files.
// See idl_gen_text.cpp.
extern std::string TextMakeRule(const Parser& parser, const std::string& path, const std::string& file_names);

// Generate a make rule for the generated binary files.
// See idl_gen_general.cpp.
extern std::string BinaryMakeRule(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate GRPC Cpp interfaces.
// See idl_gen_grpc.cpp.
bool GenerateCppGRPC(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate GRPC Go interfaces.
// See idl_gen_grpc.cpp.
bool GenerateGoGRPC(const Parser& parser, const std::string& path, const std::string& file_name);

// Generate GRPC Java classes.
// See idl_gen_grpc.cpp
bool GenerateJavaGRPC(const Parser& parser, const std::string& path, const std::string& file_name);

}    // namespace flatbuffers

#endif    // FLATBUFFERS_IDL_H_
